﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics;

namespace SimpleIR
{
	/// <summary>
	/// This class is used to represent all bytecode operations that write a value from the eval stack into memory in an indirect way (field stores, pointer stores, etc.).
	/// </summary>
	public sealed class IRStoreFromEvalStackIndirect : IRByteCode
	{
		/// <summary>
		/// An array containing all the acceptable opcodes for this operation class
		/// </summary>
		private static readonly OpCode.Op[] AcceptableOps = { OpCode.Op.ArrayStore, OpCode.Op.IndirectWrite, OpCode.Op.IndirectInitialize, OpCode.Op.StoreMemberField };

		/// <summary>
		/// This is the type that we are loading from if this is an array load.
		/// </summary>
		private readonly IRArrayType m_arraytype;

		/// <summary>
		/// This is the type of the reference we are going to store through.
		/// </summary>
		private readonly IRReferenceType m_reftype;

		/// <summary>
		/// For field store operations this is the field we are going to store to.
		/// </summary>
		private readonly IRMemberField m_storefield;

		private IRStoreFromEvalStackIndirect(OpCode.Op opc, IRArrayType arraytype, IRReferenceType storethroughptr, IRMemberField storefield, IRSourceLocation sloc)
			: base(opc, sloc)
		{
			Debug.Assert(AcceptableOps.Contains(opc));

			this.m_arraytype = arraytype;
			this.m_reftype = storethroughptr;
			this.m_storefield = storefield;
		}

		public static IRStoreFromEvalStackIndirect MakeArrayStoreOperation(IRArrayType arraytype, IRSourceLocation sloc)
		{
			Debug.Assert(arraytype != null);

			return new IRStoreFromEvalStackIndirect(OpCode.Op.ArrayStore, arraytype, null, null, sloc);
		}

		public static IRStoreFromEvalStackIndirect MakeIndirectInitOperation(IRReferenceType throughreftype, IRSourceLocation sloc)
		{
			Debug.Assert(throughreftype != null);

			return new IRStoreFromEvalStackIndirect(OpCode.Op.IndirectInitialize, null, throughreftype, null, sloc);
		}

		public static IRStoreFromEvalStackIndirect MakeStoreIndirectOperation(IRReferenceType throughreftype, IRSourceLocation sloc)
		{
			Debug.Assert(throughreftype != null);

			return new IRStoreFromEvalStackIndirect(OpCode.Op.IndirectWrite, null, throughreftype, null, sloc);
		}

		public static IRStoreFromEvalStackIndirect MakeStoreFieldOperation(IRMemberField storefield, IRSourceLocation sloc)
		{
			Debug.Assert(storefield != null);

			return new IRStoreFromEvalStackIndirect(OpCode.Op.StoreMemberField, null, null, storefield, sloc);
		}

		public override String ToString()
		{
			if(this.OperationCode == OpCode.Op.ArrayStore)
				return "starray: " + this.m_arraytype.ToString();
			else if(this.OperationCode == OpCode.Op.IndirectWrite)
				return "stind:   " + this.m_reftype.ToString();
			else if(this.OperationCode == OpCode.Op.IndirectInitialize)
				return "init :   " + this.m_reftype.ToString();
			else if(this.OperationCode == OpCode.Op.StoreMemberField)
				return "stfld:   " + this.m_storefield.ToString();
			else
				return "--BAD OPCODE--";
		}

		/// <summary>
		/// This is the array type we are going to store to.
		/// </summary>
		public IRArrayType ArrayStoreType
		{
			get
			{
				Debug.Assert(this.OperationCode == OpCode.Op.ArrayStore);
				return this.m_arraytype;
			}
		}

		/// <summary>
		/// This is the ptr type we are going to store through.
		/// </summary>
		public IRReferenceType ThroughRefType
		{
			get
			{
				Debug.Assert(this.OperationCode == OpCode.Op.IndirectWrite || this.OperationCode == OpCode.Op.IndirectInitialize);
				return this.m_reftype;
			}
		}

		/// <summary>
		/// This is the member field we are going to store into.
		/// </summary>
		public IRMemberField WriteField
		{
			get
			{
				Debug.Assert(this.OperationCode == OpCode.Op.StoreMemberField);
				return this.m_storefield;
			}
		}
	}
}

